package com.chenyun.satoken.starter.dao.fastjson2;

import cn.dev33.satoken.dao.SaTokenDao;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaFoxUtil;
import com.alibaba.fastjson2.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 直接复制官方的实现
 * @author:
 * @CreateTime: 2023-09-25  22:24
 * @Description:
 */

@Component
public class SaTokenDaoRedisFastjson2 implements SaTokenDao {
    public StringRedisTemplate stringRedisTemplate;
    public StringRedisTemplate objectRedisTemplate;
    public boolean isInit;

    public SaTokenDaoRedisFastjson2() {
    }

    @Autowired
    @Qualifier("redisConnectionFactory")
    public void init(RedisConnectionFactory connectionFactory) {
        if (!this.isInit) {
            SaStrategy.instance.createSession = (sessionId) -> {
                return new SaSessionForFastjson2Customized(sessionId);
            };
            StringRedisSerializer keySerializer = new StringRedisSerializer();
            StringRedisSerializer valueSerializer = new StringRedisSerializer();
            StringRedisTemplate stringTemplate = new StringRedisTemplate();
            stringTemplate.setConnectionFactory(connectionFactory);
            stringTemplate.afterPropertiesSet();
            StringRedisTemplate template = new StringRedisTemplate();
            template.setConnectionFactory(connectionFactory);
            template.setKeySerializer(keySerializer);
            template.setHashKeySerializer(keySerializer);
            template.setValueSerializer(valueSerializer);
            template.setHashValueSerializer(valueSerializer);
            template.afterPropertiesSet();
            this.stringRedisTemplate = stringTemplate;
            this.objectRedisTemplate = template;
            this.isInit = true;
        }
    }

    public String get(String key) {
        return (String)this.stringRedisTemplate.opsForValue().get(key);
    }

    public void set(String key, String value, long timeout) {
        if (timeout != 0L && timeout > -2L) {
            if (timeout == -1L) {
                this.stringRedisTemplate.opsForValue().set(key, value);
            } else {
                this.stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
            }

        }
    }

    public void update(String key, String value) {
        long expire = this.getTimeout(key);
        if (expire != -2L) {
            this.set(key, value, expire);
        }
    }

    public void delete(String key) {
        this.stringRedisTemplate.delete(key);
    }

    public long getTimeout(String key) {
        return this.stringRedisTemplate.getExpire(key);
    }

    public void updateTimeout(String key, long timeout) {
        if (timeout == -1L) {
            long expire = this.getTimeout(key);
            if (expire != -1L) {
                this.set(key, this.get(key), timeout);
            }

        } else {
            this.stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        }
    }

    public Object getObject(String key) {
        return this.objectRedisTemplate.opsForValue().get(key);
    }

    public SaSession getSession(String sessionId) {
        Object obj = this.getObject(sessionId);
        return obj == null ? null : (SaSession) JSON.parseObject(obj.toString(), SaSessionForFastjson2Customized.class);
    }

    public void setObject(String key, Object object, long timeout) {
        if (timeout != 0L && timeout > -2L) {
            String toValue = JSON.toJSONString(object);
            if (timeout == -1L) {
                this.objectRedisTemplate.opsForValue().set(key, toValue);
            } else {
                this.objectRedisTemplate.opsForValue().set(key, toValue, timeout, TimeUnit.SECONDS);
            }

        }
    }

    public void updateObject(String key, Object object) {
        long expire = this.getObjectTimeout(key);
        if (expire != -2L) {
            this.setObject(key, object, expire);
        }
    }

    public void deleteObject(String key) {
        this.objectRedisTemplate.delete(key);
    }

    public long getObjectTimeout(String key) {
        return this.objectRedisTemplate.getExpire(key);
    }

    public void updateObjectTimeout(String key, long timeout) {
        if (timeout == -1L) {
            long expire = this.getObjectTimeout(key);
            if (expire != -1L) {
                this.setObject(key, this.getObject(key), timeout);
            }

        } else {
            this.objectRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        }
    }

    public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
        Set<String> keys = this.stringRedisTemplate.keys(prefix + "*" + keyword + "*");
        List<String> list = new ArrayList(keys);
        return SaFoxUtil.searchList(list, start, size, sortType);
    }
}
